package ejb.soportes;

import ejb.Condicional;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

public class Consulta {

    private Object entidad;
    private TipoUnion union;
    private String operador;
    private TipoOrden ordenacion;
    private String orderBy;
    private List<Condicional> condicionales = null;
    public boolean SAFE_ENUM = true;
    public boolean USAR_LIKE = true;
    public int NUMERO_NULO = 0;

    public Consulta(Object entidad) {
        this.entidad = entidad;
        this.union = TipoUnion.AND;
        this.operador = "=";
        this.ordenacion = TipoOrden.ASC;
        this.orderBy = null;
        addFiltroEliminar();
    }

    public Consulta(Object entidad, TipoUnion union) {
        this.entidad = entidad;
        this.union = union;
        this.operador = "=";
        this.ordenacion = TipoOrden.ASC;
        this.orderBy = null;
        addFiltroEliminar();
    }

    public Consulta(Object entidad, TipoUnion union, String operador) {
        this.entidad = entidad;
        this.union = union;
        this.operador = operador;
        this.ordenacion = TipoOrden.ASC;
        this.orderBy = null;
        addFiltroEliminar();
    }

    private void addFiltroEliminar() {
        try {
            Method metodo = this.entidad.getClass().getMethod("setActivo", boolean.class);
            metodo.invoke(this.entidad, true);
        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException ex) {
        }
    }

    public Object getEntidad() {
        return entidad;
    }

    public void setEntidad(Object entidad) {
        this.entidad = entidad;
    }

    public TipoUnion getUnion() {
        return union;
    }

    public void setUnion(TipoUnion union) {
        this.union = union;
    }

    public String getOperador() {
        return operador;
    }

    public void setOperador(String operador) {
        this.operador = operador;
    }

    public TipoOrden getOrdenacion() {
        return ordenacion;
    }

    public void setOrdenacion(TipoOrden ordenacion) {
        this.ordenacion = ordenacion;
    }

    public String getOrderBy() {
        return orderBy;
    }

    public List<Condicional> getCondicionales() {
        return condicionales;
    }

    public void setCondicionales(List<Condicional> condicionales) {
        this.condicionales = condicionales;
    }

    /**
     *
     * @param orderBy : campo por donde ordenar
     */
    public void setOrderBy(String orderBy) {
        this.orderBy = orderBy;
    }

    private List<String> partes() {
        List<String> resp = new ArrayList<>();
        Class<? extends Object> clase = entidad.getClass();
        Method[] metodos = clase.getMethods();
        if (condicionales != null) {
            for (Iterator<Condicional> it = condicionales.iterator(); it.hasNext();) {
                Condicional c = it.next();
                if (c != null && c.obtenerCondicional() != null) {
                    String tmp = c.obtenerCondicional();
                    tmp = (tmp.startsWith("x.")) ? tmp.substring(2) : tmp;
                    resp.add(tmp);
                }
            }
        }
        for (int i = 0; i < metodos.length; i++) {
            Method metodo = metodos[i];
            if ((metodo.getName().startsWith("get") || metodo.getName().startsWith("is"))
                    && metodo.getParameterTypes().length == 0
                    && !void.class.equals(metodo.getReturnType())) {
                String nombre = "";
                if (metodo.getName().startsWith("get")) {
                    nombre = metodo.getName().substring(3, 4).toLowerCase()
                            + metodo.getName().substring(4);
                } else if (metodo.getName().startsWith("is")) {
                    nombre = metodo.getName().substring(2, 3).toLowerCase()
                            + metodo.getName().substring(3);
                }
                String agregar = null;
                try {
                    Object retorno = metodo.invoke(entidad, new Object[]{});
                    if (retorno == null) {
                    } else if (retorno instanceof Integer) {
                        agregar = ((Integer) retorno > NUMERO_NULO) ? nombre + this.operador + (Integer) retorno : null;
                    } else if (retorno instanceof Long) {
                        agregar = ((Long) retorno > NUMERO_NULO) ? nombre + this.operador + (Long) retorno : null;
                    } else if (retorno instanceof Double) {
                        agregar = ((Double) retorno > NUMERO_NULO) ? nombre + this.operador + (Double) retorno : null;
                    } else if (retorno instanceof Float) {
                        agregar = ((Float) retorno > NUMERO_NULO) ? nombre + this.operador + (Float) retorno : null;
                    } else if (retorno instanceof Boolean) {
                        agregar = nombre + "=" + ((Boolean) retorno);
                    } else if (retorno.getClass().isEnum()) {
                        if (!SAFE_ENUM) {
                            agregar = nombre + "=" + retorno.getClass().getName() + "." + retorno.toString();
                        }
                    } else if (retorno instanceof String) {
                        if (USAR_LIKE) {
                            agregar = nombre + " LIKE '%" + (String) retorno + "%'";
                        } else {
                            agregar = nombre + " = '" + (String) retorno + "'";
                        }
                    } else if (retorno instanceof Date) {
                    } else {
                        Class<? extends Object> c1 = retorno.getClass();
                        if (c1.isEnum()) {
                        } else {
                            try {
                                Method m1 = c1.getDeclaredMethod("getId",
                                        new Class[]{});
                                Object r1 = m1.invoke(retorno, new Object[]{});
                                if (r1 != null) {
                                    if (r1 instanceof Long) {
                                        agregar = nombre + ".id"
                                                + this.operador + (Long) r1;
                                    }
                                }
                            } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
                                // TODO: handle exception
                            }
                        }
                    }
                } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
                    // TODO Auto-generated catch block
                    // e.printStackTrace();
                }
                if (agregar != null) {
                    resp.add(agregar);
                }
            }
        }
        return resp;
    }

    @Override
    public String toString() {
        String consulta = "";
        Class<? extends Object> clase = entidad.getClass();
        consulta += "SELECT x FROM " + clase.getSimpleName() + " x";
        List<String> partes = this.partes();
        String where = "WHERE";
        TipoUnion uniont;
        boolean persisteUnion = false;
        for (Iterator<String> iterator = partes.iterator(); iterator.hasNext();) {
            String parte = (String) iterator.next();
            uniont = (parte.startsWith("activo")) ? TipoUnion.AND : this.union;
            uniont = (persisteUnion) ? TipoUnion.AND : uniont;
            persisteUnion = parte.startsWith("eliminado") && where.length() > 0;
            consulta += (where.equals("")) ? " " + uniont.toString()
                    + " x." + parte : " " + where + " x." + parte;
            where = "";
        }
        if (this.orderBy != null) {
            if (!this.orderBy.trim().isEmpty()) {
                consulta += " ORDER BY x." + this.orderBy.trim()
                        + " " + this.ordenacion.toString();
            }
        }
        return consulta;
    }

    public String countToString() {
        String consulta = "";
        Class<? extends Object> clase = entidad.getClass();
        consulta += "SELECT COUNT (x) FROM " + clase.getSimpleName() + " x";
        List<String> partes = this.partes();
        String where = "WHERE";
        for (Iterator<String> iterator = partes.iterator(); iterator.hasNext();) {
            String parte = (String) iterator.next();
            consulta += (where.equals("")) ? " " + this.union.toString()
                    + " x." + parte : " " + where + " x." + parte;
            where = "";
        }
        return consulta;
    }

    public enum TipoUnion {

        AND(0), OR(1);
        private int valor;

        private TipoUnion(int valor) {
            this.valor = valor;
        }

        public int getValor() {
            return valor;
        }

        public void setValor(int valor) {
            this.valor = valor;
        }
    }

    public enum TipoOrden {

        ASC(0), DESC(1);
        private int valor;

        private TipoOrden(int valor) {
            this.valor = valor;
        }

        public int getValor() {
            return valor;
        }

        public void setValor(int valor) {
            this.valor = valor;
        }
    }
}
